#!/usr/local/bin/python

###################################################################################
#   Copyright 2010 Jason Hoover
#
#
#   This file is part of AutoCache.
#
#   AutoCache is free software: you can redistribute it and/or modify
#   it under the terms of the GNU Lesser General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   AutoCache is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.

#   You should have received a copy of the GNU Lesser General Public License
#   along with AutoCache.  If not, see <http://www.gnu.org/licenses/>.
###################################################################################

import ac_common
from ac_common import *
import ac_cache
from ac_cache import *

import re
import threading
import sys

##########################
# ac_compiletargets - Uses re (regexp) lib and makes precompiled regexps and appends them to a list.
# this list is then checked against by the main function. I originally wanted to have seperate regexps
# per domain, but this turned out to be more efficent in the end.
#########################

def ac_compiletargets():

	# Open the file, initialize the list and read the first line.

	targets_file=file(ac_settings["targets"],'r')
	target_contents = []
	lastline = targets_file.readline().rstrip()

	# Read lines until we run out.

	while lastline:	
		try:
			target_contents.append(re.compile(lastline))			
		except re.error:
			# If a line doesn't compile, log it, but don't die.
			ac_log.error("Error compiling regular expression: "+lastline)
	
		lastline = targets_file.readline().rstrip()

	return target_contents
	targets_file.close()

#########################
# ac_concurrentrequest - A function which allows ac to support concurrencies for squid 2.5 and above.
# Concurrent requests look different than normal requests, in the format: "ID URL ip/fqdn ident method key-pairs"
# ID 		- A Unique ID of the request.
# URL 		- The incoming URL
# ip		- Request Source IP
# fqdn 		- The request source
# method	- The HTTP method.
# key-pairs	- Unknown.
#########################

def ac_concurrentrequest(request):
	try:

		# Split the request into a tuple.
		# According to squid wiki come in the format of:
		# "ID URL ip/fqdn ident method key-pairs"

		request=request.strip().split()
		ac_log.debug("Got request "+str(request))

		# If it's not a GET request, we don't give a fuck.

		if request[4] != "GET":
			print(request[0])
			ac_log.debug("Request "+str(request)+" was not a GET")

		ac_log.debug("Request: "+str(request)+"Was a GET")

		# Check the request against the regexps.

		for regexp in targets:
			if regexp.match(request[1]):
	
				# Store the cached target to see if there's anything. 
				# ac_checkup returns either the cached target, or nothing.
				# Unfortunately, python prints "False" as "False" and not a null. 
				# ffffff.

				cached_target = ac_checkup(request[1])
				if cached_target:
					
					# We want to print our ID and our cached object if we've won.
					sys.stdout.write(request[0]+" "+cached_target+"\n")
					sys.stdout.flush()
					return
				else:
					# Or we want to start a new download if we've lost.
					threading.Thread(target=ac_update,args=(request[1],)).start()
					sys.stdout.write(request[0]+" \n")
					sys.stdout.flush()
					return

		# If it matches nothing, print this.
		ac_log.debug("Returning: "+request[0])
		sys.stdout.write(request[0]+" \n")
		sys.stdout.flush()

	except ValueError:	
		ac_log.debug("Something went horribly, horribly wrong with the request: "+str(request))
		exit()
	except IndexError:
		ac_log.debug("There were values missing from: "+str(request))
	except EOFError:
		ac_log.debug("Got EOF, all done!")
		return


#########################
# main - Here we define the main loop. Note that there are two different versions.
# One which supports concurrency using threads of the ac_concurrentrequest function, and one
# which simply includes it's same logic, but with slightly different variables.
#########################

if ac_settings['concurrency']:
	def main():
		try:
			stdin=sys.stdin

			while True:
				url=stdin.readline()

				if url == '':
					ac_log.debug("STDIN was Null. Closing.")
					return

				# Don't be afraid to spawn more threads as necessary. It's up to squid.conf to
				# not feed us too many at once.
	
				threading.Thread(target=ac_concurrentrequest,args=(url,)).start()
		except ValueError:
			ac_log.debug("STDIN closed, all done!")
			return
		except:
			ac_log.debug("Some other exception. Terminating.")
			raise
			return

else:
	def main():

		try:

			

			while True:
			
				request=stdin.readline()

				try:

					# Split the request into a tuple.
					# According to squid wiki come in the format of:
					# "URL ip/fqdn ident method key-pairs"

					request=request.strip().split()
			
					# If not a GET, don't care. Don't pass go. Don't collect $200.
	
					if request[3] != "GET":
						print
	
					# Check the request against the regexps.

					for regexp in targets:
						if regexp.match(request[0]):

							# Store the cached target to see if there's anything. 
							# ac_checkup returns either the cached target, or nothing.
							# Unfortunately, python prints "False" as "False" and not a null. 
	
							cached_target = ac_checkup(request[0])
							if cached_target:
						
								# Print the cached version if we won.
								sys.stdout.write(cached_target)
								sys.stdout.flush()
							else:
								# Or we want to start a new download if we've lost.
								threading.Thread(target=ac_update,args=(request[1],)).start()
				except:	
					print
					ac_log.debug("Something went horribly wrong with the request: "+str(request))
					
		except EOFError:
			ac_log.debug("Got EOF from god, all done!")
			return

ac_log.debug("Starting AC.")
targets=ac_compiletargets()
ac_log.info("ac Started.")
main()
